using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

using Open.Data.Persistence.Hashes;

namespace Open.Data.Persistence
{
    public class Labels: Dictionary<LabelHash, string>
    {
        //Working variables
        private char _abbrev; //the character eg a
        private int _count; //The number of times we have passed z

        #region Interface

        //Constructors       
        public Labels(Type type)
        {
            ResetAbbreviations();
            AddType(new TypeLabelHash(type));
        }

        public Labels(IQuery query)
        {
            ResetAbbreviations();
            AddQuery(new QueryLabelHash(query));
        }

        //Methods
        protected virtual void ResetAbbreviations()
        {
            _abbrev = (Char) 97;
            _count = 0;
        }

        protected virtual void IncrementAbbreviations()
        {
            //Check if 'z'
            if (_abbrev == (char) 122)
            {
                _abbrev = (char) 97;
                _count++;
            }
            else
            {
                _abbrev = (char) (_abbrev + 1);
            }
        }

        protected virtual string GetAbbreviation()
        {
            if (_count == 0) return _abbrev.ToString();
            return _abbrev + _count.ToString();
        }

        #endregion

        #region Implementation

        private void AddType(TypeLabelHash label)
        {
            //Add any underlying properties
            CacheItem cache = Provider.Instance.ReflectionCache.Lookup(label.Item);
            if (!cache.IsUnderlying)
            {
                TypeLabelHash parent = new TypeLabelHash(cache.BaseType, label);
                AddType(parent);
            }

            //Add the main type
            Add(label, GetAbbreviation());
            IncrementAbbreviations();

            //Add any object properties
            if (cache.HasObjects)
            {
                foreach (PropertyInfo prop in cache.ReferenceProperties)
                {
                    PropertyLabelHash parent = new PropertyLabelHash(prop, label);
                    AddProperty(parent);
                }
            }
        }

        private void AddProperty(PropertyLabelHash label)
        {
            //Get any reference properties and add
            CacheItem cache = Provider.Instance.ReflectionCache.Lookup(label.Item.PropertyType);
            if (!cache.IsUnderlying)
            {
                TypeLabelHash parent = new TypeLabelHash(cache.BaseType, label);
                AddType(parent);
            } 
            
            //Add the label
            Add(label, GetAbbreviation());
            IncrementAbbreviations();

            //Add any object properties
            if (cache.HasObjects)
            {
                foreach (PropertyInfo prop in cache.ReferenceProperties)
                {
                    PropertyLabelHash parent = new PropertyLabelHash(prop, label);
                    AddProperty(parent);
                }
            }
        }

        private void AddQuery(QueryLabelHash label)
        {
            //Add the query type
            TypeLabelHash parent = new TypeLabelHash(label.Item.Type, label);
            AddType(parent);

            //Add any subqueries
            foreach (IQuery query in label.Item.SubQueries)
            {
                QueryLabelHash parent2 = new QueryLabelHash(query, label);
                AddQuery(parent2);
            }
        }

        #endregion
    }
}
